/*
 * UAE - The Un*x Amiga Emulator
 *
 * AutoConfig devices
 *
 * Copyright 1995, 1996 Bernd Schmidt
 * Copyright 1996 Ed Hanway
 */

#include "sysconfig.h"
#include "thread.h"
#include "newcpu.h"
#include "traps.h"
#include "filesys.h"
#include "hardfile.h"

/* Commonly used autoconfig strings */

uae_ptr EXPANSION_explibname, EXPANSION_doslibname, EXPANSION_uaeversion;
uae_ptr EXPANSION_uaedevname, EXPANSION_explibbase = 0;
uae_ptr EXPANSION_bootcode, EXPANSION_nullfunc;

/* ROM tag area memory access */

byte* rtarea;
uae_ptr rtarea_base = RTAREA_DEFAULT;

static uint REGPARAM3 rtarea_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 rtarea_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 rtarea_bget(uae_ptr) REGPARAM;
static void REGPARAM3 rtarea_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 rtarea_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 rtarea_bput(uae_ptr, uint) REGPARAM;
static byte* REGPARAM3 rtarea_xlate(uae_ptr) REGPARAM;
static int REGPARAM3 rtarea_check(uae_ptr addr, uint size) REGPARAM;

addrbank rtarea_bank = {
    rtarea_lget, rtarea_wget, rtarea_bget,
    rtarea_lput, rtarea_wput, rtarea_bput,
    rtarea_xlate, rtarea_check, nullptr, L"UAE Boot ROM",
    rtarea_lget, rtarea_wget, ABFLAG_ROMIN
};

static byte* REGPARAM2 rtarea_xlate(uae_ptr addr)
{
    addr &= 0xFFFF;
    return rtarea + addr;
}

static int REGPARAM2 rtarea_check(uae_ptr addr, uint size)
{
    addr &= 0xFFFF;
    return (addr + size) <= 0xFFFF;
}

static uint REGPARAM2 rtarea_lget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr &= 0xFFFF;
    return (uint)(rtarea_wget(addr) << 16) + rtarea_wget(addr + 2);
}

static uint REGPARAM2 rtarea_wget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr &= 0xFFFF;
    return (rtarea[addr] << 8) + rtarea[addr + 1];
}

static uint REGPARAM2 rtarea_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr &= 0xFFFF;
    return rtarea[addr];
}

static void REGPARAM2 rtarea_lput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
}

static void REGPARAM2 rtarea_wput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
}

static void REGPARAM2 rtarea_bput(uae_ptr addr, uint value)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
}

/* some quick & dirty code to fill in the rt area and save me a lot of
 * scratch paper
 */

static int rt_addr;
static int rt_straddr;

uint addr(int ptr)
{
    return (uint)ptr + rtarea_base;
}

void db(byte data)
{
    rtarea[rt_addr++] = data;
}

void dw(ushort data)
{
    rtarea[rt_addr++] = (byte)(data >> 8);
    rtarea[rt_addr++] = (byte)data;
}

void dl(uint data)
{
    rtarea[rt_addr++] = data >> 24;
    rtarea[rt_addr++] = data >> 16;
    rtarea[rt_addr++] = data >> 8;
    rtarea[rt_addr++] = data;
}

byte dbg(uae_ptr addr)
{
    addr -= rtarea_base;
    return rtarea[addr];
}

/* store strings starting at the end of the rt area and working
 * backward.  store pointer at current address
 */

uint ds_ansi(const char* str)
{
    int len;

    if (!str)
        return addr(rt_straddr);
    len = strlen(str) + 1;
    rt_straddr -= len;
    strcpy((char*)rtarea + rt_straddr, str);
    return addr(rt_straddr);
}

uint ds(const TCHAR* str)
{
    char* s = Unicode::ua(str);
    uint v = ds_ansi(s);
    free(s);
    return v;
}

void calltrap(uint n)
{
    dw(0xA000 + n);
}

void org(uint a)
{
    if (((a & 0xffff0000) != 0x00f00000) && ((a & 0xffff0000) != rtarea_base))
        Logger::Write(L"ORG: corrupt address! %08X", a);
    rt_addr = a & 0xffff;
}

uint here()
{
    return addr(rt_addr);
}

void align(int b)
{
    rt_addr = (rt_addr + b - 1) & ~(b - 1);
}

static uint REGPARAM2 nullfunc(TrapContext* context)
{
    Logger::Write(L"Null function called\n");
    return 0;
}

static uint REGPARAM2 getchipmemsize(TrapContext* context)
{
    m68k_dreg(regs, 1) = 0;
    m68k_areg(regs, 1) = 0;
    return allocated_chipmem;
}

static uint REGPARAM2 uae_puts(TrapContext* context)
{
    puts((char*)get_real_address(m68k_areg(regs, 0)));
    return 0;
}

void rtarea_init_mem()
{
    rtarea = mapped_malloc(0x10000, L"rtarea");
    if (!rtarea)
    {
        Logger::Write(L"virtual memory exhausted (rtarea)!\n");
        abort();
    }
    rtarea_bank.baseaddr = rtarea;
}

void rtarea_init()
{
    uint a;
    TCHAR uaever[100];

    rt_straddr = 0xFF00 - 2;
    rt_addr = 0;

    init_traps();

    rtarea_init_mem();
    memset(rtarea, 0, 0x10000);

    _stprintf(uaever, L"uae-%d.%d.%d", UAEMAJOR, UAEMINOR, UAESUBREV);

    EXPANSION_uaeversion = ds(uaever);
    EXPANSION_explibname = ds(L"expansion.library");
    EXPANSION_doslibname = ds(L"dos.library");
    EXPANSION_uaedevname = ds(L"uae.device");

    deftrap(nullptr);  /* Generic emulator trap */

    dw(0);
    dw(0);

    a = here();
    /* Dummy trap - removing this breaks the filesys emulation. */
    org(rtarea_base + 0xFF00);
    calltrap(deftrap2(nullfunc, TRAPFLAG_NO_RETVAL, L""));

    org(rtarea_base + 0xFF80);
    calltrap(deftrapres(getchipmemsize, TRAPFLAG_DORET, L"getchipmemsize"));

    org(rtarea_base + 0xFF10);
    calltrap(deftrapres(uae_puts, TRAPFLAG_NO_RETVAL, L"uae_puts"));
    dw(RTS);

    org(a);

    #ifdef FILESYS
    filesys_install_code();
    #endif
    #ifdef PICASSO96
    uaegfx_install_code();
    #endif

    uae_boot_rom_size = here() - rtarea_base;
    init_extended_traps();
}

volatile int uae_int_requested = 0;

void set_uae_int_flag()
{
    rtarea[0xFFFB] = uae_int_requested & 1;
}

void rtarea_setup()
{
    uae_ptr base = need_uae_boot_rom();
    if (base)
    {
        if (Logging::AUTOCONF_DEBUG)
            Logger::Write(L"RTAREA located at %08X\n", base);
        rtarea_base = base;
    }
}

uae_ptr makedatatable(uae_ptr resid, uae_ptr resname, byte type, sbyte priority, ushort ver, ushort rev)
{
    uae_ptr datatable = here();

    dw(0xE000);         /* INITBYTE */
    dw(0x0008);         /* LN_TYPE */
    dw(type << 8);
    dw(0xE000);         /* INITBYTE */
    dw(0x0009);         /* LN_PRI */
    dw(priority << 8);
    dw(0xC000);         /* INITLONG */
    dw(0x000A);         /* LN_NAME */
    dl(resname);
    dw(0xE000);         /* INITBYTE */
    dw(0x000E);         /* LIB_FLAGS */
    dw(0x0600);         /* LIBF_SUMUSED | LIBF_CHANGED */
    dw(0xD000);         /* INITWORD */
    dw(0x0014);         /* LIB_VERSION */
    dw(ver);
    dw(0xD000);         /* INITWORD */
    dw(0x0016);         /* LIB_REVISION */
    dw(rev);
    dw(0xC000);         /* INITLONG */
    dw(0x0018);         /* LIB_IDSTRING */
    dl(resid);
    dw(0x0000);         /* end of table */
    return datatable;
}